package com.secure;

import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class PGP {

	private BigInteger N = null;
	private BigInteger e = null;
	private BigInteger d = null;

	// Set the bit length of our large primes.
	public static final int RSA_KEY_SIZE = 512;

	byte[] concat(byte[] A, byte[] B) {
		byte[] C = new byte[A.length + B.length];
		System.arraycopy(A, 0, C, 0, A.length);
		System.arraycopy(B, 0, C, A.length, B.length);

		return C;
	}

	public byte[] generateAESKey() throws NoSuchAlgorithmException {
		KeyGenerator kgen = KeyGenerator.getInstance("AES");
		kgen.init(128);
		SecretKey skey = kgen.generateKey();
		byte[] raw = skey.getEncoded();
		return raw;
	}

	public byte[] decodeAES(byte[] msg, byte[] kljuc)
			throws NoSuchAlgorithmException, NoSuchPaddingException,
			InvalidKeyException, InvalidKeySpecException,
			IllegalBlockSizeException, BadPaddingException, IOException {
		String rez = "";

		
		SecretKeySpec skeySpec = new SecretKeySpec(kljuc, "AES");

		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
		
		BASE64Decoder bs = new BASE64Decoder();
		byte[] podaci = bs.decodeBuffer(new String(msg,"UTF-8"));

		byte[] utf8 = cipher.doFinal(podaci);

		//rez = new String(utf8, "UTF8");

		return utf8;
	}

	public String codeAES(byte[] utf8, byte[] kljuc)
			throws NoSuchAlgorithmException, NoSuchPaddingException,
			InvalidKeyException, InvalidKeySpecException,
			IllegalBlockSizeException, BadPaddingException, IOException {
		String rez = "";

		SecretKeySpec skeySpec = new SecretKeySpec(kljuc, "AES");

		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

		// byte[] utf8 = msg.getBytes("UTF8");
		byte[] enc = cipher.doFinal(utf8);
		BASE64Encoder bs = new BASE64Encoder();
		rez = bs.encode(enc);

		return rez;
	}

	public void generateRSAKeys() {
		BigInteger p, q, PhiN;

		// Generate two distinct large prime numbers p, q
		p = new BigInteger(RSA_KEY_SIZE, 10, new Random());
		do {
			q = new BigInteger(RSA_KEY_SIZE, 10, new Random());
		} while (q.compareTo(p) == 0);

		// Calculate their product
		N = p.multiply(q);

		// And calculate PhiN = (p-1)(q-1)
		PhiN = p.subtract(BigInteger.valueOf(1));
		PhiN = PhiN.multiply(q.subtract(BigInteger.valueOf(1)));

		// Next choose e, coprime to and less than PhiN
		do {
			e = new BigInteger(2 * RSA_KEY_SIZE, new Random());
		} while ((e.compareTo(PhiN) != -1)
				|| (e.gcd(PhiN).compareTo(BigInteger.valueOf(1)) != 0));

		// Compute d, the inverse of e mod PhiN
		d = e.modInverse(PhiN);
	}

	public String codeRSA(byte[] sym, BigInteger N, BigInteger e)
			throws IOException {
		String hex = byteToHexString(sym);

		BigInteger message = new BigInteger(hex, 16);
		;

		while ((message.compareTo(N) != -1)
				|| (message.gcd(N).compareTo(BigInteger.valueOf(1)) != 0))
			;
		BigInteger ciphertext = message.modPow(e, N);

		BASE64Encoder bs = new BASE64Encoder();
		return bs.encode(ciphertext.toByteArray());
	}

	public byte[] decodeRSA(byte[] msg, BigInteger N, BigInteger d)
			throws IOException {
		BASE64Decoder bs = new BASE64Decoder();
		byte[] podaci = bs.decodeBuffer(new String(msg,"UTF-8"));
		String hex = byteToHexString(podaci);
		BigInteger pt = new BigInteger(hex, 16);
		byte[] res = pt.modPow(d, N).toByteArray();

		if (res.length > 16) {
			byte ret[] = new byte[res.length - 1];
			for (int i = 1; i < res.length; i++) {
				ret[i - 1] = res[i];
			}
			return ret;
		} else if (res.length < 16) {
			byte ret[] = new byte[res.length + 1];
			ret[0] = 0;
			for (int i = 0; i < res.length; i++) {
				ret[i + 1] = res[i];
			}
			return ret;

		} else
			return res;

	}

	public byte[] decodeRSA(byte[] msg) throws IOException {
		return decodeRSA(msg, N, d);
	}

	public String getMD5(String msg) {

		byte[] defaultBytes = msg.getBytes();
		try {
			MessageDigest algorithm = MessageDigest.getInstance("MD5");
			algorithm.reset();
			algorithm.update(defaultBytes);
			byte messageDigest[] = algorithm.digest();

			return byteToHexString(messageDigest);
		} catch (NoSuchAlgorithmException nsae) {

		}
		return null;
	}

	public String getMD5(byte[] defaultBytes) {

		try {
			MessageDigest algorithm = MessageDigest.getInstance("MD5");
			algorithm.reset();
			algorithm.update(defaultBytes);
			byte messageDigest[] = algorithm.digest();

			return byteToHexString(messageDigest);
		} catch (NoSuchAlgorithmException nsae) {

		}
		return null;
	}

	public String byteToHexString(byte in[]) {
		StringBuffer hexString = new StringBuffer();
		for (int i = 0; i < in.length; i++) {
			String apd = Integer.toHexString(0xFF & in[i]);
			if (apd.length() < 2)
				hexString.append("0" + apd);
			else
				hexString.append(apd);
		}
		String rez = hexString.toString();
		return rez.toUpperCase();

	}

	public BigInteger getN() {
		return N;
	}

	public void setN(BigInteger n) {
		N = n;
	}

	public BigInteger getE() {
		return e;
	}

	public void setE(BigInteger e) {
		this.e = e;
	}
	
	public BigInteger getD() {
		return d;
	}

	public void setD(BigInteger d) {
		this.d = d;
	}

}
